package com.platform.common.security.permission;

import com.alibaba.fastjson2.JSON;
import com.platform.common.core.domain.base.BaseRequest;
import com.platform.common.core.domain.model.DataPermissionDTO;
import com.platform.common.core.domain.model.LoginUser;
import com.platform.common.core.domain.model.PermissionGroupDTO;
import com.platform.common.core.exception.ServiceException;
import com.platform.common.core.utils.StringUtils;
import com.platform.common.security.service.TokenService;
import com.platform.common.security.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Description: 数据权限注解处理器
 * @Author: wcs
 * @Date: 2024-10-22 14:09
 **/
@Slf4j
@Aspect
@Component
public class DataPermissionProcess {

    @Resource
    private TokenService tokenService;

    @Pointcut("@annotation(com.platform.common.security.permission.DataPermissionIntercept)")
    public void pointcut() {

    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        // 获取当前请求
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (Objects.isNull(attributes)) {
            log.error("获取请求属性失败");
            throw new ServiceException("获取请求属性失败");
        }
        Object[] args = joinPoint.getArgs();
        handlerBaseRequest(joinPoint, args);
        Object proceed;
        try {
            proceed = joinPoint.proceed(args);
        } catch (Exception e) {
            log.error("【数据权限】业务处理异常,Exception异常信息：{}", e.getMessage());
            throw new ServiceException("业务处理异常:" + e.getMessage());
        } catch (Throwable e) {
            log.error("【数据权限】业务处理异常,Throwable异常信息：{}", e.getMessage());
            throw new ServiceException("业务处理异常:" + e.getMessage());
        } finally {
            log.debug("【数据权限】切面处理完毕");
        }
        return proceed;
    }

    private void handlerBaseRequest(ProceedingJoinPoint joinPoint, Object[] args) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        DataPermissionIntercept annotation = method.getAnnotation(DataPermissionIntercept.class);
        if (Objects.isNull(annotation)) {
            throw new ServiceException("【数据权限】请正确使用数据权限注解DataPermissionIntercept");
        }
        DataPermissionStrategy strategy = annotation.dataPermissionStrategy();
        switch (strategy) {
            case FROM_USER_DATA_SOURCE_CONTROL:
                throw new ServiceException("暂不支持该数据权限策略");
            case FROM_USER_FUNCTION_DATA_PERMISSION:
                processUserFunctionDataPermission(args);
                break;
            default:
                break;
        }
    }

    private void processUserFunctionDataPermission(Object[] args) {
        // 遍历入参
        for (Object arg : args) {
            if (arg instanceof BaseRequest) {
                // 没有数据权限id的不处理
                if (Objects.isNull(((BaseRequest) arg).getPermissionRelationId())) {
                    continue;
                }
                LoginUser loginUser = SecurityUtils.getLoginUser();
                if (Objects.isNull(loginUser)) {
                    throw new ServiceException("用户登录失效");
                }
                // 后续根据用户功能对应的数据权限与请求路径比较获取最终数据权限
                processBaseRequestDataOfLoginUser((BaseRequest) arg, loginUser);
                log.debug("【数据权限】,request:{}", JSON.toJSONString(arg));
            }
        }
    }

    private void processBaseRequestDataOfLoginUser(BaseRequest baseRequest, LoginUser loginUser) {
        DataPermissionDTO dataPermission = tokenService.getDataPermissionByFunctionId(baseRequest.getPermissionRelationId(), loginUser);
        if (Objects.isNull(dataPermission) || StringUtils.isEmpty(dataPermission.getDataPermissions())) {
            log.error("用户【{}】功能Id【{}】没有配置数据权限", loginUser.getUsername(), baseRequest.getPermissionRelationId());
            return;
        }
        Class<?> aClass = baseRequest.getClass();
        List<Field> allFields = getAllFields(aClass);
        List<PermissionGroupDTO> permissions = dataPermission.getDataPermissions();
        for (PermissionGroupDTO permission : permissions) {
            String permissionKey = permission.getPermissionKey().substring(3);
            String camelCase = StringUtils.toCamelCase(permissionKey);
            // 如果单值字段有值则不再赋值List
            for (Field declaredField : allFields) {
                if (StringUtils.equals(camelCase, declaredField.getName())) {
                    try {
                        ReflectionUtils.makeAccessible(declaredField);
                        Object o = declaredField.get(baseRequest);
                        if (Objects.nonNull(o)) {
                            return;
                        }
                    } catch (Exception e) {
                        log.error("反射异常");
                    }
                }
            }
            String targetField = camelCase + "List";
            String permissionValue = permission.getPermissionValue();
            if (StringUtils.isBlank(permissionValue)) {
                log.error("用户【{}】功能Id【{}】对应的数据权限【{}】的value为空", loginUser.getUsername(), baseRequest.getPermissionRelationId(), permissionKey);
                return;
            }
            List<String> list = Arrays.asList(permissionValue.split(","));
            String singleValue = null;
            if (list.size() == 1) {
                singleValue = list.get(0);
            }
            for (Field declaredField : allFields) {
                if (StringUtils.equals(targetField, declaredField.getName())) {
                    try {
                        ReflectionUtils.makeAccessible(declaredField);
                        ReflectionUtils.setField(declaredField, baseRequest, list);
                    } catch (Exception e) {
                        log.error("反射异常");
                    }
                }
                if (StringUtils.isNotBlank(singleValue) && StringUtils.equals(camelCase, declaredField.getName())) {
                    try {
                        ReflectionUtils.makeAccessible(declaredField);
                        ReflectionUtils.setField(declaredField, baseRequest, singleValue);
                    } catch (Exception e) {
                        log.error("反射异常");
                    }
                }
            }

        }
    }


    public static List<Field> getAllFields(Class<?> clazz) {
        if (clazz == null) {
            return Collections.emptyList();
        }
        Field[] fields = clazz.getDeclaredFields();
        List<Field> parentFields = getAllFields(clazz.getSuperclass());
        return Stream.concat(Arrays.stream(fields), parentFields.stream()).collect(Collectors.toList());
    }
}
